﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using VideoReflector.Models;
using VideoReflector.Video;
using BuzzIO;

namespace VideoReflector.Controllers
{
    /// <summary>
    /// Controller tasked with creating session data and capturing session video
    /// </summary>
    public class CaptureSessionController : TaskController
    {
        #region Private fields

        private Session _session;
        private ApplicationSettings _settings;
        private ITagController _tagController;
        private IVideoRecorder _videoRecorder;
        private bool _videoIsRecorded;

        #endregion // Private fields

        #region Constructor

        /// <summary>
        /// Constructor for CaptureSessionController
        /// </summary>
        /// <param name="session"> Session object to be populated </param>
        /// <param name="settings"> Current application settings </param>
        /// <param name="videoDisplayContainer"> WPF Panel object which video output should be displayed in </param>
        /// <param name="tagController"> Tag controller object associated with session </param>
        public CaptureSessionController(Session session, ApplicationSettings settings,
                                        System.Windows.Controls.Panel videoDisplayContainer,
                                        ITagController tagController)
        {
            _session = session;
            _settings = settings;
            _tagController = tagController;
            // create session directory
            var directoryInfo = Directory.CreateDirectory(
                    Path.Combine(_settings.ExpandedDefaultSaveDirectory, _session.DateTime.ToString("yyyy-MM-dd_HH-mm-ss"))
            );
            // store session directory path
            _session.SaveDirectory =  directoryInfo.FullName;
            // save session on every property change or change in clip tags collection
            _session.PropertyChanged += (s, e) => Session.Save();
            _session.ClipTags.CollectionChanged += (s, e) => Session.Save();
            // create video recorder object
            var outputPath = System.IO.Path.Combine(_session.SaveDirectory, 
                "Video." + _settings.AudioVideoSettings.Container);
            _videoRecorder = new VlcVideoRecorder(outputPath, _settings.AudioVideoSettings,
                                                  videoDisplayContainer);
            // store session video file details
            _session.VideoFile.Location.Value = Path.GetFileName(outputPath);
            _session.VideoFile.Location.pathIsRelative = true;
            _session.VideoFile.Resolution = _settings.AudioVideoSettings.VideoOutputFormat.Resolution;
            _session.VideoFile.FrameRate = _settings.AudioVideoSettings.VideoOutputFormat.FrameRate;
            // unset video recorded flag
            _videoIsRecorded = false;
        }

        #endregion // Constructor

        #region Public properties

        /// <summary>
        /// Public accessor for session object which holds current session data
        /// </summary>
        public Session Session
        {
            get { return _session; }
        }

        /// <summary>
        /// Public accessor for video recorder object which allows control of video capture
        /// </summary>
        public IVideoRecorder VideoRecorder
        {
            get { return _videoRecorder; }
        }

        /// <summary>
        /// Public accessor for tag controller object which exposes command for creating new clip tags
        /// </summary>
        public ITagController TagController
        {
            get { return _tagController; }
        }

        #endregion // Public properties

        #region Commands

        #region StartRecording

        private RelayCommand _startRecordingCommand;

        /// <summary>
        /// Start recording of video from video device specified in application settings and enable tag controller
        /// </summary>
        public ICommand StartRecordingCommand
        {
            get
            {
                if (_startRecordingCommand == null)
                {
                    _startRecordingCommand = new RelayCommand
                    (
                        param => this.StartRecording(),
                        param => this.CanStartRecording()
                    );
                }
                return _startRecordingCommand;
            }
        }

        private void StartRecording()
        {
            VideoRecorder.Start();
            TagController.Start();
        }

        private bool CanStartRecording()
        {
            return VideoRecorder != null && !VideoRecorder.IsRecording && !_videoIsRecorded && TagController != null;
        }

        #endregion // StartRecording  

        #region StopRecording

        private RelayCommand _stopRecordingCommand;

        /// <summary>
        /// End recording from video device, finalising video file, and disable tag controller
        /// </summary>
        public ICommand StopRecordingCommand
        {
            get
            {
                if (_stopRecordingCommand == null)
                {
                    _stopRecordingCommand = new RelayCommand
                    (
                        param => this.StopRecording(),
                        param => this.CanStopRecording()
                    );
                }
                return _stopRecordingCommand;
            }
        }

        private void StopRecording()
        {
            VideoRecorder.Stop();
            TagController.Stop();
            Session.VideoFile.Length = TagController.ElapsedTime;
            _videoIsRecorded = true;
        }

        private bool CanStopRecording()
        {
            return VideoRecorder != null && VideoRecorder.IsRecording && !_videoIsRecorded;
        }

        #endregion // StopRecording  

        #region ReviewSession

        private RelayCommand _reviewSessionCommand;

        /// <summary>
        /// Begin reviewing current session
        /// </summary>
        public ICommand ReviewSessionCommand
        {
            get
            {
                if (_reviewSessionCommand == null)
                {
                    _reviewSessionCommand = new RelayCommand
                    (
                        param => this.ReviewSession(),
                        param => this.CanReviewSession()
                    );
                }
                return _reviewSessionCommand;
            }
        }

        private void ReviewSession()
        {
            Session.Save();
            OnFinished(new TaskFinishedEventArgs(false));
        }

        private bool CanReviewSession()
        {
            return Session != null && VideoRecorder != null && !VideoRecorder.IsRecording;
        }

        #endregion // ReviewSession  

        #region DeleteSession

        private RelayCommand _deleteSessionCommand;

        /// <summary>
        /// End current session and attempt to delete all associated files
        /// </summary>
        public ICommand DeleteSessionCommand
        {
            get
            {
                if (_deleteSessionCommand == null)
                {
                    _deleteSessionCommand = new RelayCommand
                    (
                        param => this.DeleteSession(),
                        param => this.CanDeleteSession()
                    );
                }
                return _deleteSessionCommand;
            }
        }

        private void DeleteSession()
        {
            if (MessageBox.Show(
                    "Are you sure you want to delete all files associated with this session?",
                    "Confirm Session Delete",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Question,
                    MessageBoxResult.No) == MessageBoxResult.Yes)
            {
                VideoRecorder.Stop();
                VideoRecorder.Dispose();
                // TODO: Video file cannot be deleted as remains open in libVLC process even after nVLC objects disposed
                // - need to find some way of releasing objects cleanly 
                try { Directory.Delete(_session.SaveDirectory, true); }
                catch (IOException) 
                {
                    MessageBox.Show("Not all session files could be deleted from directory " + _session.SaveDirectory
                                    + " and will need to be removed manually when the application is closed",
                                    "File(s) not deleted", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
                OnFinished(new TaskFinishedEventArgs(true));
            }
        }

        private bool CanDeleteSession()
        {
            return Session != null;
        }

        #endregion // DeleteSession  
                                      
        #endregion // Commands
    }
}
